package gitee.blacol.chineseTime.core;

import gitee.blacol.chineseTime.entity.ChineseConsts;
import gitee.blacol.chineseTime.entity.ChineseTime;
import gitee.blacol.chineseTime.entity.TransferMode;
import gitee.blacol.chineseTime.exception.FormatException;
import gitee.blacol.chineseTime.util.ChineseNumberUtil;

import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import static gitee.blacol.chineseTime.util.DateUtil.getWesternDate;
import static gitee.blacol.chineseTime.util.DateUtil.getWesternTime;

/**
 * <p>NewTimeTransfer class.</p>
 * 时间转换器类
 * @author Blacol
 */
public class NewTimeTransfer {
    /**
     * 以公历推算干支纪年，<br>
     * 符合传统的计算方法<br>
     * 传统计算方法中月份的推算是按照节令推算的（每年立春是新一年的开始）<br>
     * 为了方便计算，规定每年各个节气的日期固定（正常情况下每年节气的日期是不固定的）
     *
     * @param date 公历日期
     * @param mode 模式（共5种，见类<code>com.blacol.chineseTime.entity.TransferMode</code>）
     * @return 干支纪年法时间
     */
    public static ChineseTime westernToChinese(Date date, TransferMode mode){
        switch (mode){
            case 日期:return onlyDate(date);
            case 传统日期时间:return traditionalDateTime(date);
            case 传统日期时间_截止到时辰:return traditionalDateHour(date);
            case 完整的日期时间_简记:return fullDateTime_simple(date);
            default: {
                System.out.println("模式不正确！");
                return null;
            }
        }
    }

    private static ChineseTime fullDateTime_simple(Date date) {
        ChineseTime chineseTime = simpleDateTime(date);
        计算时地支(date,chineseTime);
        计算时辰符号(date,chineseTime);
        chineseTime.设分钟(ChineseNumberUtil.numberToChinese(date.getMinutes()));
        chineseTime.设秒(ChineseNumberUtil.numberToChinese(date.getSeconds()));
        chineseTime.设模式(TransferMode.完整的日期时间_简记);
        return chineseTime;
    }

    private static ChineseTime traditionalDateHour(Date date) {
        ChineseTime chineseTime = simpleDateTime(date);
        计算时地支(date,chineseTime);
        chineseTime.设模式(TransferMode.传统日期时间_截止到时辰);
        return chineseTime;
    }

    /**
     * 根据指定格式转换干支纪年时间
     *
     * @param date 公历日期
     * @param format 指定的日期格式
     * @return 干支纪年法时间
     * @throws gitee.blacol.chineseTime.exception.FormatException 日期格式不正确
     */
    public static ChineseTime westernToChinese(Date date, String format) throws FormatException {
        ChineseTime onlyDate = onlyDate(date);
        onlyDate.设模式(TransferMode.指定格式);
        return switch (format){
            case "年月日"->onlyDate;
            case "年月日 小时"->{
                计算时天干(date, onlyDate);
                计算时地支(date,onlyDate);
                计算时辰符号(date,onlyDate);
                yield onlyDate;
            }
            case "年月日 小时分钟"->{
                计算时天干(date,onlyDate);
                计算时地支(date, onlyDate);
                计算时辰符号(date,onlyDate);
                onlyDate.设分钟(ChineseNumberUtil.numberToChinese(date.getMinutes()));
                yield onlyDate;
            }
            case "年月日 小时分钟秒"->{
                计算时天干(date, onlyDate);
                计算时地支(date, onlyDate);
                计算时辰符号(date,onlyDate);
                onlyDate.设分钟(ChineseNumberUtil.numberToChinese(date.getMinutes()));
                onlyDate.设秒(ChineseNumberUtil.numberToChinese(date.getSeconds()));
                yield onlyDate;
            }
            default-> {
                throw new FormatException("""
                        格式化字符串不正确，只支持下面四种：
                        1. 年月日
                        2. 年月日 小时
                        3. 年月日 小时分钟
                        4. 年月日 小时分钟秒
                        """);
            }
        };
    }

    private static void 计算时天干(Date date, ChineseTime simpleDateTime) {
        char[] 日干支 = 计算日干支(date);
        int 时天干索引 = switch (日干支[0]) {
            case '甲', '己' -> 1;
            case '乙', '庚' -> 3;
            case '丙', '辛' -> 5;
            case '丁', '壬' -> 7;
            case '戊', '癸' -> 9;
            default -> '\u0000';
        };
        int hours = date.getHours();
        时天干索引+=(Math.round(hours/2.0)%10);
        时天干索引%=10;
        simpleDateTime.设时天干(ChineseConsts.天干[时天干索引]);
    }
    private static void 计算时地支(Date date, ChineseTime simpleDateTime) {
        int 时地支索引=1;
        int hours = date.getHours();
        时地支索引+=(Math.round(hours/2.0)%12);
        时地支索引%=12;
        simpleDateTime.设时地支(ChineseConsts.地支[时地支索引]);
    }
    private static void 计算时辰符号(Date date, ChineseTime simpleDateTime) {
        int hours = date.getHours();
        if (hours==23||hours==0){
            simpleDateTime.设时辰符号('\u0000');
        }else {
            simpleDateTime.设时辰符号(ChineseConsts.时辰符号[hours%2]);
        }
    }

    private static ChineseTime onlyDate(Date date){
        ChineseTime chineseTime=new ChineseTime();
        char[] 年干支 = 计算年干支(date);
        char[] 月干支 = 计算月干支(date,年干支[0]);
        char[] 日干支 = 计算日干支(date);
        return 设置日期(chineseTime, 年干支, 月干支, 日干支);
    }

    static ChineseTime 设置日期(ChineseTime chineseTime, char[] 年干支, char[] 月干支, char[] 日干支) {
        chineseTime.设年干(年干支[0]);
        chineseTime.设年支(年干支[1]);
        chineseTime.设月干(月干支[0]);
        chineseTime.设月支(月干支[1]);
        chineseTime.设日干(日干支[0]);
        chineseTime.设日支(日干支[1]);
        chineseTime.设模式(TransferMode.日期);
        return chineseTime;
    }

    private static char[] 计算日干支(Date date){
        int[] 月基数表={0,31,-1,30,0,31,1,32,3,33,4,34};
        int[] westernDate=getWesternDate(date);
        int 天干序列;
        int 世纪=westernDate[0]/100+1;
        int 世纪常数= (int) ((44*(世纪-17)+Math.floor((世纪-17)/4)+3)%60);
        int 公元年数后两位=westernDate[0]%100;
        int r=(公元年数后两位-1)/4*6+5*((公元年数后两位-1)/4*3+(公元年数后两位-1)%4)+月基数表[westernDate[1]-1]+westernDate[2]+世纪常数;
        if (westernDate[0]!=0&&westernDate[0]%4==0&&westernDate[0]%400!=0){
            r=r+1;
        }
        int 六十花甲子序数=r%60;
        天干序列=六十花甲子序数%10;
        if (天干序列==10){
            天干序列=0;
        }
        char 日干=ChineseConsts.天干[天干序列];
        int 地支序列=六十花甲子序数%12;
        if (地支序列==12){
            地支序列=0;
        }
        char 日支=ChineseConsts.地支[地支序列];
        return new char[]{日干,日支};
    }

    private static char[] 计算月干支(Date date,char 年干) {
        int[] westernDate=getWesternDate(date);
        int 天干索引 = 0;
        天干索引=switch (年干){
            case '甲', '己'->3;
            case '乙', '庚'->5;
            case '丙', '辛'->7;
            case '丁', '壬'->9;
            case '戊', '癸'->1;
            default -> -1;
        };
        int 地支索引=0;

        int 月=westernDate[1];
        int 日=westernDate[2];
        if (月==1&&日<5) {
            地支索引 = 1;
        }else if (月==2&&日<4||月==1){
            地支索引=2;
        }else{
            地支索引=3;
        }
        Map<String, Integer[]> 节气表 = ChineseConsts.取节气表();
        Set<String> strings = 节气表.keySet();
        Iterator<String> iterator = strings.iterator();
        int 节气索引=-1;
        Integer[] 节气日期=new Integer[2];
        while (iterator.hasNext()){
            if (节气索引+1==月-1){
                String next = iterator.next();
                Integer[] integers = 节气表.get(next);
                节气日期=integers;
                break;
            }else{
                节气索引++;
                iterator.next();
            }
        }
        if (日<节气日期[1]&&月>2){
            月=月-1;
        }
        if(月==1&&日<5){
            天干索引 += 10;
        }else if (月==1||(月==2&&日<4)){
            天干索引+=11;
        }else{
            if (月==2){
                地支索引+=月-2;
            }else{
                地支索引+=月-2;
                天干索引+=月-2;
            }
        }
        char 月干=ChineseConsts.天干[天干索引%10];
        char 月支=ChineseConsts.地支[地支索引%12];
        return new char[]{月干,月支};
    }

    private static char[] 计算年干支(Date date) {
        int[] westernDate = getWesternDate(date);
        Map<String, Integer[]> 取节气表 = ChineseConsts.取节气表();
        Integer[] 立春日期 = 取节气表.get("立春");
        if (westernDate[1]<立春日期[0]||westernDate[1]==立春日期[0]&&westernDate[2]<立春日期[1]){
            westernDate[0]-=1;
        }
        char 年干=ChineseConsts.天干[(westernDate[0]-3)%10];
        char 年支 = ChineseConsts.地支[(westernDate[0] - 3) % 12];
        return new char[]{年干,年支};
    }

    private static ChineseTime simpleDateTime(Date date){
        ChineseTime chineseTime = onlyDate(date);
        int[] westernTime = getWesternTime(date);
        计算时天干(date,chineseTime);
        chineseTime.设分钟(String.valueOf(westernTime[1]));
        chineseTime.设秒(String.valueOf(westernTime[2]));
        return chineseTime;
    }
    private static ChineseTime traditionalDateTime(Date date){
        ChineseTime chineseTime = simpleDateTime(date);
        计算时天干(date,chineseTime);
        计算时地支(date,chineseTime);
        计算时辰符号(date,chineseTime);
        chineseTime.设分钟(ChineseNumberUtil.numberToChinese(date.getMinutes()));
        chineseTime.设秒(ChineseNumberUtil.numberToChinese(date.getSeconds()));
        chineseTime.设模式(TransferMode.传统日期时间);
        return chineseTime;
    }
}
